﻿using jbt.CodeCandy.AsTrEAM.Contracts;
using System;
using System.IO;
using System.Threading.Tasks;

namespace jbt.CodeCandy.AsTrEAM.StreamManager
{
    /// <summary>
    /// Implements an <see cref="IStreamManager"/> object what manages files in a directory.
    /// </summary>
    public class FileStreamManager : IStreamManager
    {
        private readonly string _rootDirectory;

        /// <summary>
        /// Initializes a new instance of the <see cref="FileStreamManager"/> class with a root directory.
        /// </summary>
        /// <param name="rootDirectory">
        /// The new instance manages files in this directory.
        /// </param>
        /// <exception cref="ArgumentException">
        /// <paramref name="rootDirectory"/> is null or white space.
        /// </exception>
        /// <exception cref="DirectoryNotFoundException">
        /// The path <paramref name="rootDirectory"/> refers to doesn't exist.
        /// </exception>
        public FileStreamManager(string rootDirectory)
        {
            if (String.IsNullOrWhiteSpace(rootDirectory))
            {
                throw new ArgumentException("Root directory is null or white space.", "rootDirectory");
            }

            if (!Directory.Exists(rootDirectory))
            {
                var message = String.Format("Directory '{0}' doesn't exist.", rootDirectory);

                throw new DirectoryNotFoundException(message);
            }

            _rootDirectory = rootDirectory;
        }

        private async Task<NewStreamResult> CreateFile(StreamData fileData)
        {
            try
            {
                var fileName = Path.GetFileName(fileData.Name);

                var newFile = new NewStreamResult
                {
                    Id = fileName,
                };

                var path = Path.Combine(_rootDirectory, fileName);

                using (var fileStream = System.IO.File.OpenWrite(path))
                {
                    await fileData.Content.CopyToAsync(fileStream);
                }

                return newFile;
            }
            catch (Exception exception)
            {
                var message = String.Format("Failed to create file '{0}'. {1}", fileData.Name, exception.Message);

                throw new StreamManagerException(message, exception);
            }
        }

        private StreamData GetFile(string id)
        {
            try
            {
                var fileName = id;

                var path = Path.Combine(_rootDirectory, fileName);

                var fileInfo = new FileInfo(path);

                var stream = new AuditStream(id, fileInfo.OpenRead());

                var fileData = new StreamData
                {
                    Content = stream,
                    ContentLength = fileInfo.Length,
                    Name = fileName,
                    Id = id,
                    LastModified = fileInfo.LastWriteTimeUtc,
                };

                return fileData;
            }
            catch (Exception exception)
            {
                var message = String.Format("Failed to read file '{0}'. {1}", id, exception.Message);

                throw new StreamManagerException(message, exception);
            }
        }

        #region IStreamManager Members

        async Task<NewStreamResult> IStreamManager.Create(StreamData fileData)
        {
            if (fileData == null)
            {
                throw new ArgumentNullException("fileData");
            }

            var newFile = await CreateFile(fileData);

            return newFile;
        }

        async Task<StreamData> IStreamManager.Get(string id)
        {
            if (String.IsNullOrWhiteSpace(id))
            {
                throw new ArgumentException("Id is null or white space.", "id");
            }

            var fileDataTask = Task.Run(() => GetFile(id));

            return await fileDataTask;
        }

        #endregion
    }
}